package com.tubeonfire.entity;

import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import javax.jdo.annotations.PersistenceCapable;
import javax.jdo.annotations.Persistent;
import javax.jdo.annotations.PrimaryKey;

import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.select.Elements;

import com.google.appengine.api.datastore.Text;
import com.google.gdata.data.youtube.CommentEntry;
import com.google.gdata.data.youtube.CommentFeed;
import com.google.gdata.data.youtube.VideoEntry;
import com.google.gdata.data.youtube.VideoFeed;
import com.google.gdata.util.ServiceException;
import com.tubeonfire.model.CategoryModel;
import com.tubeonfire.model.TagModel;
import com.tubeonfire.model.TubeModel;
import com.tubeonfire.service.TubeService;
import com.tubeonfire.util.JavaCacheHandle;
import com.tubeonfire.util.StringHelper;

@PersistenceCapable
public class Tube {

	public void print() {
		System.out.println("Tube Id : " + this.tubeId);
		System.out.println("Category : " + this.categoryName);
		System.out.println("Published : " + this.published);
		System.out.println("Updated : " + this.updated);
		System.out.println("Title : " + this.title);
		System.out.println("Sub title : " + this.subTitle);
		System.out.println("Description : " + this.description);
		System.out.println("Alias : " + this.alias);
		System.out.println("ThumbImageUrl : " + this.thumbImageUrl);
		System.out.println("Duration : " + this.duration);
		System.out.println("TotalTime : " + this.totalTime);
		System.out.println("RelatedTubes : ");
		if (this.relatedTubes != null) {
			for (Text relate : this.relatedTubes) {
				System.out.println(relate.getValue());
			}
		} else {
			System.out.println("null");
		}
		System.out.println("Keywords : ");
		if (this.keywords != null) {
			for (Text keyword : this.keywords) {
				System.out.println(keyword.getValue());
			}
		} else {
			System.out.println("null");
		}
		System.out.println("Comments : ");
		if (this.comments != null) {
			for (Text comment : this.comments) {
				System.out.println(comment.getValue());
			}
		} else {
			System.out.println("null");
		}

		System.out.println("TotalComment : " + this.totalComment);
		System.out.println("Author Name : " + this.authorName);
		System.out.println("Author Url : " + this.authorUrl);
		System.out.println("View Count : " + this.viewCount);
		System.out.println("Number Likes : " + this.numLikes);
		System.out.println("Number dis Likes : " + this.numDisLikes);
		System.out.println("Number rate : " + this.numRate);
		System.out.println("Min rate : " + this.minRate);
		System.out.println("Max rate : " + this.maxRate);
		System.out.println("Average Rate : " + this.averageRate);
		System.out.println("Status : " + this.status);
	}

	@PrimaryKey
	@Persistent
	private String tubeId; // id from youtube, use to get video.
	@Persistent
	private String categoryName;
	@Persistent
	private Date published; // created date from youtube.
	@Persistent
	private Date updated; // update date.
	@Persistent
	private Text title; // video title.
	@Persistent
	private Text subTitle; // title with shorter length. Substring from title.
							// Used to display in search result.
	@Persistent
	private Text description; // video description.
	@Persistent
	private Text alias; // video alias, from title replace string.
	@Persistent
	private Text thumbImageUrl; // image will display in search result.
	@Persistent
	private Long duration; // video duration, how many seconds.
	@Persistent
	private Text totalTime; // calculate by duration. example : 2:36:30.
	@Persistent
	private List<Text> relatedTubes;
	@Persistent
	private List<Text> keywords; // video tags.
									// videoEntry.getMediaGroup().getKeywords().getKeywords()
	@Persistent
	private List<Text> otherKeywords; // video other tags.
	@Persistent
	private List<Text> comments;
	@Persistent
	private Text totalComment; // comment count;
	@Persistent
	private Text authorName; // author name.
	@Persistent
	private String authorUrl; // author url.
	@Persistent
	private Text viewCount; // total view of video.
	@Persistent
	private Text numLikes; // number of people like.
	@Persistent
	private Text numDisLikes; // number of people dislike.
	@Persistent
	private Text numRate; // video rate.
	@Persistent
	private Text minRate; // min point of rate.
	@Persistent
	private Text maxRate; // max point of rate.
	@Persistent
	private Text averageRate; // average point.
	@Persistent
	private int status;

	public List<Text> getOtherKeywords() {
		return otherKeywords;
	}

	public void setOtherKeywords(List<Text> otherKeywords) {
		this.otherKeywords = otherKeywords;
	}

	public String getTubeId() {
		return tubeId;
	}

	public void setTubeId(String tubeId) {
		this.tubeId = tubeId;
	}

	public String getCategoryName() {
		return categoryName;
	}

	public void setCategoryName(String categoryName) {
		this.categoryName = categoryName;
	}

	public Date getPublished() {
		return published;
	}

	public void setPublished(Date published) {
		this.published = published;
	}

	public Date getUpdated() {
		return updated;
	}

	public void setUpdated(Date updated) {
		this.updated = updated;
	}

	public Text getTitle() {
		return title;
	}

	public void setTitle(Text title) {
		this.title = title;
	}

	public Text getSubTitle() {
		return subTitle;
	}

	public void setSubTitle(Text subTitle) {
		this.subTitle = subTitle;
	}

	public Text getDescription() {
		return description;
	}

	public void setDescription(Text description) {
		this.description = description;
	}

	public Text getAlias() {
		return alias;
	}

	public void setAlias(Text alias) {
		this.alias = alias;
	}

	public Text getThumbImageUrl() {
		return thumbImageUrl;
	}

	public void setThumbImageUrl(Text thumbImageUrl) {
		this.thumbImageUrl = thumbImageUrl;
	}

	public Long getDuration() {
		return duration;
	}

	public void setDuration(Long duration) {
		this.duration = duration;
	}

	public Text getTotalTime() {
		return totalTime;
	}

	public void setTotalTime(Text totalTime) {
		this.totalTime = totalTime;
	}

	public List<Text> getRelatedTubes() {
		return relatedTubes;
	}

	public void setRelatedTubes(List<Text> relatedTubes) {
		this.relatedTubes = relatedTubes;
	}

	public List<Text> getKeywords() {
		return keywords;
	}

	public void setKeywords(List<Text> keywords) {
		this.keywords = keywords;
	}

	public List<Text> getComments() {
		return comments;
	}

	public void setComments(List<Text> comments) {
		this.comments = comments;
	}

	public Text getTotalComment() {
		return totalComment;
	}

	public void setTotalComment(Text totalComment) {
		this.totalComment = totalComment;
	}

	public Text getAuthorName() {
		return authorName;
	}

	public void setAuthorName(Text authorName) {
		this.authorName = authorName;
	}

	public String getAuthorUrl() {
		return authorUrl;
	}

	public void setAuthorUrl(String authorUrl) {
		this.authorUrl = authorUrl;
	}

	public Text getViewCount() {
		return viewCount;
	}

	public void setViewCount(Text viewCount) {
		this.viewCount = viewCount;
	}

	public Text getNumLikes() {
		return numLikes;
	}

	public void setNumLikes(Text numLikes) {
		this.numLikes = numLikes;
	}

	public Text getNumDisLikes() {
		return numDisLikes;
	}

	public void setNumDisLikes(Text numDisLikes) {
		this.numDisLikes = numDisLikes;
	}

	public Text getNumRate() {
		return numRate;
	}

	public void setNumRate(Text numRate) {
		this.numRate = numRate;
	}

	public Text getMinRate() {
		return minRate;
	}

	public void setMinRate(Text minRate) {
		this.minRate = minRate;
	}

	public Text getMaxRate() {
		return maxRate;
	}

	public void setMaxRate(Text maxRate) {
		this.maxRate = maxRate;
	}

	public Text getAverageRate() {
		return averageRate;
	}

	public void setAverageRate(Text averageRate) {
		this.averageRate = averageRate;
	}

	public int getStatus() {
		return status;
	}

	public void setStatus(int status) {
		this.status = status;
	}

	public String generateAlias(String title) {
		String alias = title;
		return alias;
	}

	/*
	 * check if this key had been search more than 1 day, if >= 1 day return
	 * false, if < 1 day return true.
	 */
	public boolean isUpdate() {
		if (this.updated != null) {
			Calendar today = Calendar.getInstance();
			Calendar updated = Calendar.getInstance();
			updated.setTime(this.updated);
			if ((today.getTimeInMillis() - updated.getTimeInMillis())
					/ (60 * 60 * 24000) >= 10) {
				return false;
			}
			return true;
		}
		return false;
	}

	/*
	 * This method convert Tube object to String. Need when save relate Videos
	 * to Appengine DB. Object fields will be saved in String with XML Format.
	 */
	public String toString() {
		StringBuilder content = new StringBuilder();
		if (this.tubeId != null) {
			content.append("<tubeId>" + this.tubeId + "</tubeId>\n");
		}
		if (this.thumbImageUrl != null) {
			content.append("<thumbImageUrl>" + this.thumbImageUrl.getValue()
					+ "</thumbImageUrl>\n");
		}
		if (this.title != null) {
			content.append("<title>" + this.title.getValue() + "</title>\n");
		}
		if (this.alias != null) {
			content.append("<alias>" + this.alias.getValue() + "</alias>\n");
		}
		if (this.authorName != null) {
			content.append("<authorName>" + this.authorName.getValue()
					+ "</authorName>\n");
		}
		if (this.viewCount != null) {
			content.append("<viewCount>" + this.viewCount.getValue()
					+ "</viewCount>\n");
		}
		if (this.totalTime != null) {
			content.append("<totalTime>" + this.totalTime.getValue()
					+ "</totalTime>\n");
		}
		return content.toString();
	}

	/*
	 * This method tranform String with xml format to Tube object (Use java
	 * regex) Be used when get relate Videos from DB to display.
	 */
	public void transformString(String tubeString) {
		Document doc = Jsoup.parse(tubeString);
		Elements tmp;
		tmp = doc.select("tubeId");
		if (tmp != null) {
			this.tubeId = (tmp.text());
		}
		tmp = doc.select("thumbImageUrl");
		if (tmp != null) {
			this.thumbImageUrl = (new Text(tmp.text()));
		}
		tmp = doc.select("title");
		if (tmp != null) {
			this.title = (new Text(tmp.text()));
		}
		if (this.title.getValue().length() > 33) {
			String[] split = this.title.getValue().substring(0, 33).split(" ");
			String subTitle = "";
			for (int i = 0; i < (split.length - 1); i++) {
				subTitle += " " + split[i];
			}
			if (subTitle.length() > 33) {
				subTitle = subTitle.substring(0, 33);
			}
			this.subTitle = new Text(subTitle + "...");
		} else {
			this.subTitle = this.title;
		}
		tmp = doc.select("alias");
		if (tmp != null) {
			this.alias = (new Text(tmp.text()));
		}
		tmp = doc.select("authorName");
		if (tmp != null) {
			this.authorName = (new Text(tmp.text()));
		}
		tmp = doc.select("viewCount");
		if (tmp != null) {
			try {
				DecimalFormat nf = (DecimalFormat) DecimalFormat.getInstance();
				nf.setDecimalSeparatorAlwaysShown(true);
				Long view = Long.parseLong(tmp.text());
				this.viewCount = (new Text(nf.format(view)));
			} catch (Exception e) {
				this.viewCount = (new Text("1"));
			}
		}
		tmp = doc.select("totalTime");
		if (tmp != null) {
			this.totalTime = (new Text(tmp.text()));
		}
	}

	/*
	 * This method tranform VideoEntry (from Gdata - Youtube api) to Tube
	 * Object.
	 */
	public void transformVideoEntry(VideoEntry videoEntry)
			throws MalformedURLException, IOException, ServiceException {

		this.published = (new Date(videoEntry.getPublished().getValue()));
		this.updated = (Calendar.getInstance().getTime());
		if (videoEntry.getRelatedVideosLink() != null) {
			List<Text> related = new ArrayList<Text>();
			List<Tube> listSave2DB = new ArrayList<Tube>();
			VideoFeed feeds = TubeService.service.getFeed(new URL(videoEntry
					.getRelatedVideosLink().getHref()), VideoFeed.class);
			for (VideoEntry entry : feeds.getEntries()) {
				Tube item = new Tube();
				item.transformHalfVideoEntry(entry);
				item.setStatus(2);
				item.setUpdated(Calendar.getInstance().getTime());
				related.add(new Text(item.toString()));
				if (TubeModel.getByTubeId(item.getTubeId()) == null) {
					listSave2DB.add(item);
					System.out.println(item.getTitle() + " ready to save !");
				}
			}
			if (listSave2DB.size() > 0) {
				System.out.println("Save : " + listSave2DB.size()
						+ " videos success !");
				TubeModel.addAll(listSave2DB);
			}
			this.relatedTubes = (related);
		}
		JavaCacheHandle.initCache();
		if (videoEntry.getMediaGroup().getKeywords() != null) {
			List<Text> listKey = new ArrayList<Text>();
			for (String keyword : videoEntry.getMediaGroup().getKeywords()
					.getKeywords()) {
				Tag tmpTag = null;
				listKey.add(new Text(keyword));
				TagModel tagModel = new TagModel();
				String tagAlias = keyword;
				tmpTag = tagModel.getByAlias(tagAlias);
				List<Text> listTubeId = new ArrayList<Text>();
				if (tmpTag != null) {
					tmpTag.setCount(tmpTag.getCount() + 1);
					listTubeId.addAll(tmpTag.getListTubeId());
					listTubeId.add(new Text(this.tubeId));
					tmpTag.setListTubeId(listTubeId);
					tagModel.update(tmpTag);
					tagModel.closePM();
				} else {
					tmpTag = new Tag();
					listTubeId.add(new Text(this.tubeId));
					tmpTag.setListTubeId(listTubeId);
					tmpTag.setAlias(tagAlias);
					tmpTag.setCount(1);
					tmpTag.setTitle(new Text(tagAlias));
					tmpTag.setType("N");
					tagModel.add(tmpTag);
				}
			}
			this.keywords = listKey;
			JavaCacheHandle.addKeywords(listKey);
		}

		// get other tag.
		this.otherKeywords = JavaCacheHandle.getRandomKeywords();

		if (videoEntry.getComments() != null) {
			List<Text> listComment = new ArrayList<Text>();
			CommentFeed commentFeed = TubeService.service.getFeed(new URL(
					videoEntry.getComments().getFeedLink().getHref()),
					CommentFeed.class);
			for (CommentEntry cmtEntry : commentFeed.getEntries()) {
				StringBuilder content = new StringBuilder();
				content.append("<commentId>" + cmtEntry.getId()
						+ "</commentId>\n");
				content.append("<content>" + cmtEntry.getPlainTextContent()
						+ "</content>\n");
				content.append("<author>"
						+ cmtEntry.getAuthors().get(0).getName()
						+ "</author>\n");
				content.append("<punished>"
						+ new Date(cmtEntry.getPublished().getValue())
						+ "</punished>\n");
				listComment.add(new Text(content.toString()));
			}
			this.comments = (listComment);
			this.totalComment = (new Text((videoEntry.getComments()
					.getFeedLink().getCountHint()).toString()));
		}
		if (videoEntry.getStatistics() != null) {
			this.viewCount = (new Text(String.valueOf(videoEntry
					.getStatistics().getViewCount())));
		}
		if (videoEntry.getYtRating() != null) {
			this.numLikes = (new Text(String.valueOf(videoEntry.getYtRating()
					.getNumLikes())));
			this.numDisLikes = (new Text(String.valueOf(videoEntry
					.getYtRating().getNumDislikes())));
		}
		if (videoEntry.getRating() != null) {
			this.numRate = (new Text(String.valueOf(videoEntry.getRating()
					.getNumRaters())));
			this.minRate = (new Text(String.valueOf(videoEntry.getRating()
					.getMin())));
			this.maxRate = (new Text(String.valueOf(videoEntry.getRating()
					.getMax())));
			this.averageRate = (new Text(String.valueOf(videoEntry.getRating()
					.getAverage())));
		}

		this.status = (1);
		this.updated = (Calendar.getInstance().getTime());
	}

	/*
	 * This method tranform only important field VideoEntry (from youtube api)
	 * to Tube
	 */
	public void transformHalfVideoEntry(VideoEntry entry) {
		this.tubeId = (entry.getMediaGroup().getVideoId());

		if (entry.getMediaGroup().getThumbnails() != null
				&& entry.getMediaGroup().getThumbnails().size() > 1) {
			this.thumbImageUrl = (new Text(entry.getMediaGroup()
					.getThumbnails().get(1).getUrl()));
		}
		this.title = (new Text(entry.getMediaGroup().getTitle()
				.getPlainTextContent()));

		if (this.title.getValue().length() > 33) {
			String[] split = this.title.getValue().substring(0, 33).split(" ");
			String subTitle = "";
			for (int i = 0; i < (split.length - 1); i++) {
				subTitle += " " + split[i];
			}
			if (subTitle.length() > 33) {
				subTitle = subTitle.substring(0, 33);
			}
			this.subTitle = new Text(subTitle + "...");
		} else {
			this.subTitle = this.title;
		}
		if (entry.getMediaGroup().getDescription() != null) {
			this.description = (new Text(entry.getMediaGroup().getDescription()
					.getPlainTextContent()));
		}
		this.alias = (new Text(StringHelper.getAliasByLanguage(this.getTitle()
				.getValue())));

		this.authorName = (new Text(entry.getAuthors().get(0).getName()));
		this.authorUrl = (entry.getAuthors().get(0).getUri());
		if (entry.getStatistics() != null) {
			this.viewCount = (new Text(String.valueOf(entry.getStatistics()
					.getViewCount())));
		}
		if (entry.getMediaGroup().getDuration() != null) {
			Long duration = entry.getMediaGroup().getDuration();
			int hours = (int) (duration / 3600);
			int remainder = (int) (duration - hours * 3600);
			int minutes = remainder / 60;
			remainder = remainder - minutes * 60;
			int second = remainder;
			this.duration = (duration);
			this.totalTime = (new Text(((hours > 0) ? (hours + ":") : "")
					+ ((minutes > 9) ? minutes : ("0" + minutes)) + ":"
					+ ((second > 9) ? second : ("0" + second))));
		}

		this.categoryName = (entry.getMediaGroup().getCategories().get(0)
				.getLabel());
		Category cate = CategoryModel.getByCateAlias(StringHelper
				.replace(this.categoryName));
		List<Text> listTubes = new ArrayList<Text>();
		if (cate != null) {
			boolean had = false;
			listTubes = cate.getListTubes();
			for (Text textTube : listTubes) {
				if (textTube.getValue().equalsIgnoreCase(this.toString())) {
					had = true;
				}
			}
			if (!had) {
				listTubes.add(new Text(this.toString()));
				cate.setListTubes(listTubes);
				cate.setCount(cate.getCount() + 1);
				CategoryModel.update(cate);
				CategoryModel.closePM();
			}
		} else {
			cate = new Category();
			cate.setAlias(StringHelper.replace(this.categoryName));
			cate.setTitle(new Text(this.categoryName));
			cate.setCount(1);
			cate.setStatus(1);
			listTubes.add(new Text(this.toString()));
			cate.setListTubes(listTubes);
			CategoryModel.add(cate);
		}

	}
}
